#include "Sprite.h"
#include <assert.h>

CSprite::CSprite(void):spriteHeight(0), spriteWidth(0), pntSprite(NULL)
{}

CSprite::~CSprite(void)
{
	delete this->pntSprite;
}

/*function that initialise a sprite object. It return false if it fails or it return true.*/
bool CSprite::initialise(const string &adrSprite)
{
	if(!HAPI->LoadTexture(adrSprite, &this->pntSprite, &this->spriteWidth, &this->spriteHeight))
	{
		//display a message error
		HAPI->UserMessage("Error : the texture : "+adrSprite+" cannot be load", "ERROR");
		return false;
	}
	return true;
}

/*function that blit a sprite on a screen.*/
bool CSprite::blitSprite(BYTE* const screen, int positionX,  int positionY,   int screenWidth, 
							int screenHeight, bool transparency)const
{
	//Assert : test if the variables got good values
	assert(positionX>=0);
	assert(positionY>=0);
	assert(screenWidth>0);
	assert(screenHeight>0);
	assert(this->spriteWidth>0);
	assert(this->spriteHeight>0);
	assert(this->spriteWidth<=screenWidth);
	assert(this->spriteHeight<=screenHeight);
	assert(positionX<=screenWidth-this->spriteWidth);
	assert(positionY<=screenHeight-this->spriteHeight);

	const int offsetXInByte=positionX*4;
	const int screenWidthInByte=4*screenWidth;
	const int textureWidthInByte=4*this->spriteWidth;

	if(transparency==true) //if we ue the transparency
	{
		int textureRow=0;
		for(int i=positionY; i<this->spriteHeight+positionY;i++)
		{
			for(int j=0; j<this->spriteWidth;j++)
			{			
				int red=offsetXInByte+(i*screenWidthInByte)+(j*4);
				int green=red+1;
				int blue=green+1;
				
				int pixelTexture=(textureRow*textureWidthInByte)+(j*4);
				int alphaPixelTexture=this->pntSprite[pixelTexture+3];
				// if the pixel got no transparency
				if(alphaPixelTexture==255)
				{
					BYTE* destination=screen+red;
					BYTE* source=pntSprite+pixelTexture;
					memcpy(destination,source,3);
				}
				// if the pixel got transparency
				else if (alphaPixelTexture>0)
				{
					screen[red]=(BYTE)(screen[red]+((alphaPixelTexture*(pntSprite[pixelTexture]-screen[red]))>>8));
					screen[green]=(BYTE)(screen[green]+((alphaPixelTexture*(pntSprite[pixelTexture+1]-screen[green]))>>8));
					screen[blue]=(BYTE)(screen[blue]+((alphaPixelTexture*(pntSprite[pixelTexture+2]-screen[blue]))>>8));
				}		
			}
			textureRow++;
		}
		return true;
	}
	else //if we don't use transparency
	{
		BYTE* source=screen+positionX*4*positionY;
		int offsetLine=0;
		for(int i=0; i<this->spriteHeight;i++)
		{
			memcpy(source,this->pntSprite+offsetLine,this->spriteWidth*4);
			source+=screenWidthInByte;
			offsetLine+=textureWidthInByte;
		}
		return true;
	}
}

/*function that blit a sprite with the transparency given in parameter*/
bool CSprite::blitSpriteBlending(BYTE* const screen, int positionX,  int positionY, int screenWidth, 
							int screenHeight, int transparency)const
{
	//Assert : test if the variables got good values
	assert(positionX>=0);
	assert(positionY>=0);
	assert(screenWidth>0);
	assert(screenHeight>0);
	assert(this->spriteWidth>0);
	assert(this->spriteHeight>0);
	assert(this->spriteWidth<=screenWidth);
	assert(this->spriteHeight<=screenHeight);
	assert(positionX<=screenWidth-this->spriteWidth);
	assert(positionY<=screenHeight-this->spriteHeight);

	
	const int offsetXInByte=positionX*4;
	const int screenWidthInByte=4*screenWidth;
	const int textureWidthInByte=4*this->spriteWidth;
	const int sizeOfPixel=4;

	int textureRow=0;
	for(int i=positionY; i<this->spriteHeight+positionY;i++)
	{
		for(int j=0; j<this->spriteWidth;j++)
		{		
			//calcul the position in the screen
			int red=offsetXInByte+(i*screenWidthInByte)+(j*sizeOfPixel);
			int green=red+1;
			int blue=green+1;
			
			//calcul the position in the texture
			int redTexture=(textureRow*textureWidthInByte)+(j*sizeOfPixel);
			int greenTexture=(textureRow*textureWidthInByte)+(j*sizeOfPixel)+1;
			int blueTexture=(textureRow*textureWidthInByte)+(j*sizeOfPixel)+2;
			
			//blitting with transparency
			screen[red]=(BYTE)(screen[red]+((transparency*(pntSprite[redTexture]-screen[red]))>>8));
			screen[green]=(BYTE)(screen[green]+((transparency*(pntSprite[greenTexture]-screen[green]))>>8));
			screen[blue]=(BYTE)(screen[blue]+((transparency*(pntSprite[blueTexture]-screen[blue]))>>8));			
		}
		textureRow++;
	}
	return true;
}

/*blit a texture clipped. That is to say only the part represented by source is blitted.*/
bool CSprite::blitTextureClipped(BYTE* const screen, int positionX, int positionY, int screenWidth, 
									int screenHeight, CRectangle source, bool transparency)const
{
//Assert : test if the variables got good values
	assert(positionX>=0);
	assert(positionY>=0);
	assert(screenWidth>0);
	assert(screenHeight>0);
	assert(this->spriteWidth>0);
	assert(this->spriteHeight>0);

	const int offsetXInByte=positionX*4;
	const int screenWidthInByte=4*screenWidth;
	const int textureWidthInByte=4*this->spriteWidth;

	
	int newTextureWidth=source.getRight()-source.getLeft();
	int newTextureHeight=source.getBottom()-source.getTop();
	BYTE* newTexturePnt = pntSprite +((4*spriteWidth)*source.getTop()+source.getLeft()*4);

	int textureRow=0;
	if (transparency==true) //if we use transparency
	{
		for(int i=positionY; i<newTextureHeight+positionY;i++) //row of the screen
		{
			for(int j=0; j<newTextureWidth;j++)  //pixel of the row
			{			
				//calcul the position of the channel in the screen
				int red=offsetXInByte+(i*screenWidthInByte)+(j*4);
				int green=red+1;
				int blue=green+1;
				
				//get the position current pixel of the texture and its transparency
				int pixelTexture=(textureRow*textureWidthInByte)+(j*4);
				int alphaPixelTexture=newTexturePnt[pixelTexture+3];

				if(alphaPixelTexture==255) //if the pixel got no transparency
				{
					BYTE* destination=screen+red;
					BYTE* source=newTexturePnt+pixelTexture;
					memcpy(destination,source,3);
				}
				else if(alphaPixelTexture>0)// if the texture got transparency
				{
					screen[red]=(BYTE)(screen[red]+((alphaPixelTexture*(newTexturePnt[pixelTexture]-screen[red]))>>8));
					screen[green]=(BYTE)(screen[green]+((alphaPixelTexture*(newTexturePnt[pixelTexture+1]-screen[green]))>>8));
					screen[blue]=(BYTE)(screen[blue]+((alphaPixelTexture*(newTexturePnt[pixelTexture+2]-screen[blue]))>>8));
				}
			}
			textureRow++;
		}
		return true;
	}
	else //if we don't use transparency
	{
		BYTE* destination=screen+(positionX*4)+(positionY*screenWidthInByte);
		BYTE* source=newTexturePnt;

		for(int i=0; i<newTextureHeight;i++)
		{
			memcpy(destination,source,newTextureWidth*4);
			destination+=screenWidthInByte;
			source+=this->spriteWidth*4;
		}
		return true;
	}
}

/*blit a clipped texture with the transparency given in parameter*/
bool CSprite::blitTextureClippedBlending(BYTE* const screen, int positionX, int positionY, int screenWidth, 
									int screenHeight, CRectangle source, int transparency)const
{
	//Assert : test if the variables got good values
	assert(positionX>=0);
	assert(positionY>=0);
	assert(screenWidth>0);
	assert(screenHeight>0);
	assert(this->spriteWidth>0);
	assert(this->spriteHeight>0);
	
	const int offsetXInByte=positionX*4;
	const int screenWidthInByte=4*screenWidth;
	const int textureWidthInByte=4*this->spriteWidth;

	int newTextureWidth=source.getRight()-source.getLeft();
	int newTextureHeight=source.getBottom()-source.getTop();
	BYTE* newTexturePnt = this->pntSprite +((4*this->spriteWidth)*source.getTop()+source.getLeft()*4);

	int textureRow=0;
	for(int i=positionY; i<newTextureHeight+positionY;i++) //row of the screen
	{
		for(int j=0; j<newTextureWidth;j++)  //pixel of the row
		{			
			//calcul the position of the channel in the screen
			int red=offsetXInByte+(i*screenWidthInByte)+(j*4);
			int green=red+1;
			int blue=green+1;
			//calcul the position of the channel in the texture
			int pixelTexture=(textureRow*textureWidthInByte)+(j*4);
			int alphaPixelTexture=newTexturePnt[pixelTexture+3];

			if(alphaPixelTexture>0)
			{
				screen[red]=(BYTE)(screen[red]+((transparency*(newTexturePnt[pixelTexture]-screen[red]))>>8));
				screen[green]=(BYTE)(screen[green]+((transparency*(newTexturePnt[pixelTexture+1]-screen[green]))>>8));
				screen[blue]=(BYTE)(screen[blue]+((transparency*(newTexturePnt[pixelTexture+2]-screen[blue]))>>8));
			}		
		}
		textureRow++;
	}
	return true;
}
//gettor
BYTE* CSprite::getPntSprite(void)const
{
	return this->pntSprite;
}

int CSprite::getSpriteWidth(void)const
{
	return this->spriteWidth;
}

int CSprite::getSpriteHeight(void)const
{
	return this->spriteHeight;
}

int CSprite::getWidthFrame(void)const
{
	return this->widthFrame;
}

int CSprite::getHeightFrame(void)const
{
	return this->heightFrame;
}
	
int CSprite::getNbOfFrame(void)const
{
	return this->nbOfFrame;
}

int CSprite::getNbOfFramePerRow(void)const
{
	return this->nbOfFramePerRow;
}

//settor
void CSprite::setPntSprite(BYTE* pointer)
{
	this->pntSprite=pointer;
}

void CSprite::setSpriteWidth(int spriteWidth)
{
	this->spriteWidth=spriteWidth;
}

void CSprite::setSpriteHeight(int spriteHeight)
{
	this->spriteHeight=spriteHeight;
}

void CSprite::setWidthFrame(int widthFrame)
{
	this->widthFrame=widthFrame;
}

void CSprite::setHeightFrame(int heightFrame)
{
	this->heightFrame=heightFrame;
}

void CSprite::setNbOfFrame(int nbOfFrame)
{
	this->nbOfFrame=nbOfFrame;
}

void CSprite::setNbOfFramePerRow(int nbOfFramePerRow)
{
	this->nbOfFramePerRow=nbOfFramePerRow;
}
